#!/usr/bin/env python3
"""
卫星态势对象数据导入脚本
从 Excel 文件读取卫星数据并导入到 PostgreSQL 数据库
"""

import sys
import os
import logging
import asyncio
import json
from pathlib import Path
from datetime import datetime
from typing import Dict, Any, List

# 尝试导入 pandas 和 openpyxl，如果没有则提供备选方案
try:
    import pandas as pd
    HAS_PANDAS = True
except ImportError:
    HAS_PANDAS = False
    logging.warning("pandas 未安装，将尝试使用其他方式读取Excel文件")

try:
    import openpyxl
    HAS_OPENPYXL = True
except ImportError:
    HAS_OPENPYXL = False
    logging.warning("openpyxl 未安装，将尝试使用其他方式读取Excel文件")

# 添加项目根目录到 Python 路径
project_root = Path(__file__).parent.parent
sys.path.insert(0, str(project_root))

# 直接导入所需模块，避免触发 src.__init__ 中的图数据库初始化
import src.situation.database
import src.situation.models
import src.situation.service
import src.situation.schemas

from src.situation.database import get_db_session, engine, Base
from src.situation.models import SituationObject
from src.situation.service import SituationService
from src.situation.schemas import SituationObjectCreate, DeploymentStatus, ObjectType


# 配置日志
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler('logs/satellite_import.log', encoding='utf-8'),
        logging.StreamHandler()
    ]
)
logger = logging.getLogger(__name__)


def read_excel_file(file_path: str) -> List[Dict[str, Any]]:
    """
    读取 Excel 文件并返回数据列表

    Args:
        file_path: Excel 文件路径

    Returns:
        包含卫星数据的字典列表
    """
    try:
        if HAS_PANDAS and HAS_OPENPYXL:
            # 使用 pandas 读取 Excel 文件
            df = pd.read_excel(file_path)
            logger.info(f"使用 pandas 成功读取 Excel 文件，共 {len(df)} 行数据")

            # 转换为字典列表
            data = df.to_dict('records')

            # 记录列名
            logger.info(f"Excel 文件包含的列: {list(df.columns)}")

            return data
        else:
            # 使用 openpyxl 直接读取
            if not HAS_OPENPYXL:
                raise ImportError("需要安装 pandas 和 openpyxl 来读取 Excel 文件")

            from openpyxl import load_workbook

            wb = load_workbook(filename=file_path, read_only=True)
            sheet = wb.active

            # 读取标题行
            headers = [cell.value for cell in next(sheet.iter_rows(min_row=1, max_row=1))]
            logger.info(f"Excel 文件包含的列: {headers}")

            # 读取数据行
            data = []
            for row in sheet.iter_rows(min_row=2, values_only=True):
                if any(row):  # 跳过空行
                    row_dict = {headers[i]: row[i] for i in range(len(headers)) if i < len(row)}
                    data.append(row_dict)

            wb.close()
            logger.info(f"使用 openpyxl 成功读取 Excel 文件，共 {len(data)} 行数据")
            return data

    except Exception as e:
        logger.error(f"读取 Excel 文件失败: {e}")
        if not HAS_PANDAS or not HAS_OPENPYXL:
            logger.error("请安装必要的依赖: uv add pandas openpyxl")
        raise


def convert_to_situation_object(satellite_data: Dict[str, Any]) -> Dict[str, Any]:
    """
    将卫星数据转换为态势对象格式

    Args:
        satellite_data: 卫星原始数据

    Returns:
        符合态势对象模型的数据字典
    """
    try:
        # 基础信息映射 - 匹配Excel的实际列名
        name = satellite_data.get('名称') or satellite_data.get('卫星名称') or satellite_data.get('name')
        code = satellite_data.get('编码') or satellite_data.get('卫星编码') or satellite_data.get('code')
        model = satellite_data.get('型号') or satellite_data.get('卫星型号') or satellite_data.get('model')
        organization = satellite_data.get('所属单位') or satellite_data.get('运营方') or satellite_data.get('organization')

        # 如果没有编码，使用名称生成编码
        if not code and name:
            # 从名称中提取英文部分作为编码
            import re
            english_part = re.search(r'\(([^)]+)\)', name)
            if english_part:
                code = english_part.group(1)
            else:
                # 使用时间戳+随机数生成唯一编码
                import random
                code = f"SAT-{datetime.now().strftime('%Y%m%d%H%M%S%f')}-{random.randint(1000, 9999)}"

        # 如果没有名称，使用编码作为名称
        if not name and code:
            name = f"卫星-{code}"
        elif not name and not code:
            # 如果都没有，生成一个默认名称
            name = f"未知卫星-{datetime.now().strftime('%Y%m%d%H%M%S')}"
            code = f"SAT-{datetime.now().strftime('%Y%m%d%H%M%S')}"

        # 处理发射时间（Excel中的列名是"发射时间"）
        launch_time = satellite_data.get('发射时间') or satellite_data.get('launch_time')
        if launch_time and hasattr(launch_time, 'strftime'):
            # 如果是datetime对象，转换为字符串
            launch_time = launch_time.strftime('%Y-%m-%d')
        elif launch_time:
            launch_time = str(launch_time)

        # 处理重访时间（Excel中的列名是"重访"）
        revisit_time_raw = satellite_data.get('重访') or satellite_data.get('重访时间') or satellite_data.get('revisit_time')
        revisit_time = parse_float_field(revisit_time_raw)

        # 处理幅宽（Excel中的列名是"幅宽"）
        swath_width_raw = satellite_data.get('幅宽') or satellite_data.get('swath_width')
        swath_width = parse_float_field(swath_width_raw)

        # 处理典型用途（Excel中的列名是"典型用途"）
        typical_usage = satellite_data.get('典型用途') or satellite_data.get('应用场景') or satellite_data.get('typical_usage')

        # 从典型用途中提取适用场景
        if typical_usage:
            applicable_scenarios = parse_list_field(typical_usage)
        else:
            applicable_scenarios = ['遥感监测', '地理测绘', '环境监测']

        # 构建态势对象数据
        situation_data = {
            'object_type': ObjectType.OBSERVATION_EQUIPMENT,  # 使用枚举类型
            'equipment_category': '卫星',  # 装备类别为卫星
            'name': name,
            'code': code,
            'model': model,
            'organization': organization,
            'deployment_status': DeploymentStatus.DEPLOYED,  # 使用枚举而不是字符串

            # 卫星特定数据存储在 type_specific_data 中
            'type_specific_data': {
                'equipment_category': '卫星',

                # 卫星专属字段
                'launch_time': launch_time,
                'sensor_type': satellite_data.get('传感器类型') or satellite_data.get('sensor_type') or '',
                'spatial_resolution': satellite_data.get('空间分辨率') or satellite_data.get('spatial_resolution') or '',
                'spectral_bands': parse_list_field(satellite_data.get('光谱波段') or satellite_data.get('spectral_bands')),
                'swath_width': swath_width,
                'revisit_time': revisit_time,
                'typical_usage': typical_usage or '',

                # 通用观测装备字段
                'detection_range': parse_float_field(satellite_data.get('探测距离') or satellite_data.get('detection_range')),
                'frequency': satellite_data.get('工作频率') or satellite_data.get('frequency') or '',
                'resolution': satellite_data.get('分辨率') or satellite_data.get('resolution') or '',

                # 其他可能的相关字段
                'orbital_altitude': parse_float_field(satellite_data.get('轨道高度') or satellite_data.get('orbital_altitude')),
                'orbital_inclination': parse_float_field(satellite_data.get('轨道倾角') or satellite_data.get('orbital_inclination')),
                'launch_site': satellite_data.get('发射场') or satellite_data.get('launch_site') or '',
                'launch_vehicle': satellite_data.get('运载火箭') or satellite_data.get('launch_vehicle') or '',
                'status': satellite_data.get('状态') or satellite_data.get('status') or '在轨',
                'country': organization or satellite_data.get('国家') or satellite_data.get('country') or '',
                'operator': organization or satellite_data.get('运营商') or satellite_data.get('operator') or '',
            },

            # 适用场景
            'applicable_scenarios': applicable_scenarios,

            # 标签
            'tags': ['卫星', '观测装备', '遥感'],

            # 其他元数据
            'extra_metadata': {
                'data_source': 'excel_import',
                'import_time': datetime.now().isoformat(),
                'original_data': str(satellite_data)  # 简化原始数据存储
            },

            # 审计信息
            'created_by': 'satellite_import_script',
            'updated_by': 'satellite_import_script'
        }

        # 处理位置信息
        location_info = {}
        if satellite_data.get('位置') or satellite_data.get('location'):
            location_info['description'] = str(satellite_data.get('位置') or satellite_data.get('location'))

        if satellite_data.get('轨道') or satellite_data.get('orbit'):
            location_info['orbit'] = str(satellite_data.get('轨道') or satellite_data.get('orbit'))

        if location_info:
            situation_data['location'] = location_info

        logger.debug(f"成功转换卫星数据: {name} (编码: {code})")
        return situation_data

    except Exception as e:
        logger.error(f"转换卫星数据失败: {e}")
        logger.error(f"原始数据: {satellite_data}")
        raise


def parse_list_field(field_value: Any) -> List[str]:
    """
    解析列表字段

    Args:
        field_value: 字段值

    Returns:
        字符串列表
    """
    if field_value is None:
        return []

    if isinstance(field_value, list):
        return [str(item).strip() for item in field_value if item]

    if isinstance(field_value, str):
        # 尝试按分隔符分割
        separators = [',', '，', ';', '；', '|', '、']
        for sep in separators:
            if sep in field_value:
                return [item.strip() for item in field_value.split(sep) if item.strip()]

        # 如果没有分隔符，作为单个元素
        return [field_value.strip()] if field_value.strip() else []

    return [str(field_value)]


def parse_float_field(field_value: Any) -> float:
    """
    解析浮点数字段

    Args:
        field_value: 字段值

    Returns:
        浮点数或 None
    """
    if field_value is None or field_value == '':
        return None

    try:
        if isinstance(field_value, (int, float)):
            return float(field_value)

        if isinstance(field_value, str):
            # 移除可能的单位和其他字符
            import re
            # 提取数字部分（包括小数点和负号）
            numbers = re.findall(r'-?\d+\.?\d*', field_value)
            if numbers:
                # 如果有多个数字，取第一个
                cleaned = numbers[0]
                return float(cleaned)

            logger.warning(f"字符串中未找到有效数字: {field_value}")
            return None

    except (ValueError, TypeError) as e:
        logger.warning(f"无法解析浮点数: {field_value}, 错误: {e}")
        return None

    return None


async def import_satellite_data(excel_file_path: str, batch_size: int = 10):
    """
    导入卫星数据到数据库

    Args:
        excel_file_path: Excel 文件路径
        batch_size: 批量处理大小
    """
    logger.info(f"=" * 80)
    logger.info(f"开始导入卫星数据")
    logger.info(f"文件路径: {excel_file_path}")
    logger.info(f"=" * 80)

    try:
        # 读取 Excel 文件
        satellite_data_list = read_excel_file(excel_file_path)
        logger.info(f"成功读取 Excel 文件，共 {len(satellite_data_list)} 条卫星数据")

        # 首先初始化数据库（确保表结构正确）
        logger.info("正在初始化数据库...")
        from src.situation.database import init_db
        await init_db()
        logger.info("数据库初始化完成")

        # 创建数据库会话
        async for db in get_db_session():
            success_count = 0
            error_count = 0
            skipped_count = 0
            error_details = []

            for i, satellite_data in enumerate(satellite_data_list, 1):
                try:
                    logger.info(f"\n{'=' * 60}")
                    logger.info(f"处理第 {i}/{len(satellite_data_list)} 条数据")

                    # 转换为态势对象格式
                    situation_data = convert_to_situation_object(satellite_data)

                    logger.info(f"卫星名称: {situation_data['name']}")
                    logger.info(f"卫星编码: {situation_data.get('code', '无')}")
                    logger.info(f"所属单位: {situation_data.get('organization', '无')}")

                    # 直接使用 ObjectType.OBSERVATION_EQUIPMENT 创建对象
                    obj_create = SituationObjectCreate(**situation_data)

                    # 创建态势对象 (SituationService.create_object 已经内置了编码重复检查)
                    created_object = await SituationService.create_object(
                        db, obj_create, user="satellite_import_script"
                    )
                    logger.info(f"✓ 成功创建态势对象: {created_object.name} (ID: {created_object.id})")
                    success_count += 1

                except ValueError as e:
                    error_msg = str(e)
                    if "已存在" in error_msg or "重复" in error_msg:
                        logger.warning(f"✗ 数据已存在，跳过: {error_msg}")
                        skipped_count += 1
                        error_details.append({
                            'index': i,
                            'name': satellite_data.get('名称', '未知'),
                            'reason': '数据已存在',
                            'error': error_msg
                        })
                    else:
                        logger.error(f"✗ 数据验证错误: {error_msg}")
                        error_count += 1
                        error_details.append({
                            'index': i,
                            'name': satellite_data.get('名称', '未知'),
                            'reason': '数据验证失败',
                            'error': error_msg
                        })
                except Exception as e:
                    logger.error(f"✗ 处理第 {i} 条数据失败: {e}")
                    error_count += 1
                    error_details.append({
                        'index': i,
                        'name': satellite_data.get('名称', '未知'),
                        'reason': '处理失败',
                        'error': str(e)
                    })
                    continue

            # 输出导入摘要
            logger.info(f"\n{'=' * 80}")
            logger.info("导入完成！")
            logger.info(f"{'=' * 80}")
            logger.info(f"总计: {len(satellite_data_list)} 条")
            logger.info(f"成功: {success_count} 条")
            logger.info(f"跳过: {skipped_count} 条（已存在）")
            logger.info(f"失败: {error_count} 条")
            logger.info(f"{'=' * 80}")

            # 如果有错误，输出错误详情
            if error_details:
                logger.info("\n错误详情：")
                for detail in error_details:
                    logger.info(f"  第 {detail['index']} 条 - {detail['name']}: {detail['reason']}")
                    logger.debug(f"    错误信息: {detail['error']}")

            return {
                'total': len(satellite_data_list),
                'success': success_count,
                'skipped': skipped_count,
                'error': error_count,
                'error_details': error_details
            }

    except Exception as e:
        logger.error(f"导入过程中发生严重错误: {e}")
        import traceback
        logger.error(traceback.format_exc())
        raise


async def main():
    """主函数"""
    # 默认文件路径
    default_file = "test/data-situation/weixing.xlsx"

    # 检查命令行参数
    if len(sys.argv) > 1:
        excel_file = sys.argv[1]
    else:
        excel_file = default_file

    # 检查文件是否存在
    if not os.path.exists(excel_file):
        logger.error(f"文件不存在: {excel_file}")
        logger.info("用法: python scripts/import_satellite_data.py [excel_file_path]")
        sys.exit(1)

    try:
        # 确保日志目录存在
        os.makedirs("logs", exist_ok=True)

        # 导入数据
        await import_satellite_data(excel_file)

        logger.info("卫星数据导入成功完成！")

    except Exception as e:
        logger.error(f"导入失败: {e}")
        sys.exit(1)


def run_main():
    """运行主函数的同步包装器"""
    asyncio.run(main())


if __name__ == "__main__":
    run_main()